package com.liustar.oplog.aspect;


import com.alibaba.fastjson.JSON;
import com.liustar.mq.rocketmq.RocketMQProducer;
import com.liustar.oplog.annotation.OpLog;
import com.liustar.oplog.model.OperationLog;
import org.apache.commons.lang3.StringUtils;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.common.RemotingHelper;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.security.Principal;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;


/**
 * @author Leo
 */
@Aspect
public class OpLogAspect {
    private final Logger logger = Logger.getLogger(OpLogAspect.class.getName());

    private String mqTopic;

    private String mqTag;

    private String mqKey;

    private RocketMQProducer mqProducer;

    @Autowired
    private InterfaceOpLog interfaceOpLog;

    @Pointcut("@annotation(com.liustar.oplog.annotation.OpLog)")
    public void controllerAspect() {
    }

    @Before(value = "controllerAspect()")
    public void doBeforeInServiceLayer(JoinPoint joinPoint) {
        logger.log(Level.INFO, "doBeforeInServiceLayer");
    }

    @After(value = "controllerAspect()")
    public void doAfterInServiceLayer(JoinPoint joinPoint) {
        logger.log(Level.INFO, "doAfterInServiceLayer");
    }

    @Around(value = "controllerAspect()")
    public Object doAround(ProceedingJoinPoint point) throws Throwable {
        logger.log(Level.INFO, "oplog =======================");
        long startTime = System.currentTimeMillis();
        // 获取request
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
        HttpServletRequest request = servletRequestAttributes.getRequest();

        // 从注解中获取操作名称、获取响应结果
        Object result = point.proceed();
        Signature signature = point.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();


        Object[] args = point.getArgs();
        OperationLog operationLog = new OperationLog();
        for (Object arg : args) {
            if (arg instanceof ServletRequest) {
                continue;
            }
            if (arg instanceof OperationLog) {
                operationLog = (OperationLog) arg;
                break;
            }
        }

        if (method.isAnnotationPresent(OpLog.class)) {
            OpLog opLog = method.getAnnotation(OpLog.class);
            operationLog.setOpType(opLog.opType());
            operationLog.setDescription(opLog.opDesc());
        }

        long endTime = System.currentTimeMillis();
        logger.log(Level.INFO, "doAround>>>result=" + result + ",耗时：" + (endTime - startTime) + "ms");

        operationLog.setBasePath(getBasePath(request));
        operationLog.setClientIp(getClientIP(request));
        String targetMethod = signature.getDeclaringTypeName() + "." + signature.getName();
        operationLog.setTargetMethod(targetMethod);
        operationLog.setRequestMethod(request.getMethod());
        if (request.getMethod().equalsIgnoreCase("post")) {
            operationLog.setRequestBody(JSON.toJSONString(request.getParameterMap()));
        } else {
            operationLog.setParameter(request.getQueryString());
        }
        operationLog.setResult(JSON.toJSONString(result));
        operationLog.setSpendTime(endTime - startTime);
        operationLog.setStartTime(new Date());
        operationLog.setRequestUri(request.getRequestURI());
        operationLog.setUserAgent(request.getHeader("User-Agent"));
        if (StringUtils.isEmpty(operationLog.getUsername())) {
            Principal principal = request.getUserPrincipal();
            if (null != principal && StringUtils.isEmpty(principal.getName())) {
                operationLog.setUsername(principal.getName());
            }
        }
        if (StringUtils.isEmpty(operationLog.getUsername())) {
            operationLog.setUsername(interfaceOpLog.getName(request));
        }

        interfaceOpLog.doOpLog(operationLog);

        return result;
    }

    private String getUsernameByToken(HttpServletRequest request) {
        return "Token";
    }

    private String getUsernameBySession(HttpServletRequest request) {
        return "Session";
    }

    private String getClientIP(HttpServletRequest request) {
        String[] headers = {"X-Forwarded-For", "X-Real-IP", "Proxy-Client-IP", "WL-Proxy-Client-IP", "HTTP_CLIENT_IP", "HTTP_X_FORWARDED_FOR"};

        String ip;
        for (String header : headers) {
            ip = request.getHeader(header);
            if (!StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
                return getMultistageReverseProxyIp(ip);
            }
        }

        ip = request.getRemoteAddr();
        return getMultistageReverseProxyIp(ip);
    }

    /**
     * 从多级反向代理中获得第一个非unknown IP地址
     *
     * @param ip 获得的IP地址
     * @return 第一个非unknown IP地址
     */
    private static String getMultistageReverseProxyIp(String ip) {
        // 多级反向代理检测
        if (ip != null && ip.indexOf(",") > 0) {
            final String[] ips = ip.trim().split(",");
            for (String subIp : ips) {
                if (!StringUtils.isBlank(subIp) || "unknown".equalsIgnoreCase(subIp)) {
                    ip = subIp;
                    break;
                }
            }
        }
        return ip;
    }

    /**
     * 获取请求basePath
     *
     * @param request
     * @return
     */
    private String getBasePath(HttpServletRequest request) {
        String uri = request.getRequestURI();
        String url = request.getRequestURL().toString();
        return url.replace(uri, "");
    }

    public String getMqTopic() {
        return mqTopic;
    }

    public void setMqTopic(String mqTopic) {
        this.mqTopic = mqTopic;
    }

    public String getMqTag() {
        return mqTag;
    }

    public void setMqTag(String mqTag) {
        this.mqTag = mqTag;
    }

    public String getMqKey() {
        return mqKey;
    }

    public void setMqKey(String mqKey) {
        this.mqKey = mqKey;
    }

    public RocketMQProducer getMqProducer() {
        return mqProducer;
    }

    public void setMqProducer(RocketMQProducer mqProducer) {
        this.mqProducer = mqProducer;
    }
}
